﻿using System;
using System.Linq;
using Engine;
using Game;
using Game.NetWork;
using GameEntitySystem;
using TemplatesDatabase;

namespace MFTrain
{
    public class ComponentMFTrain : Component, IUpdateable
    {
        private static readonly Vector3 center = new Vector3(0.5f, 0f, 0.5f);

        private static readonly Quaternion[] directions = new Quaternion[4]
        {
            Quaternion.CreateFromAxisAngle(Vector3.UnitY, 0f),
            Quaternion.CreateFromAxisAngle(Vector3.UnitY, (float)Math.PI / 2f),
            Quaternion.CreateFromAxisAngle(Vector3.UnitY, (float)Math.PI),
            Quaternion.CreateFromAxisAngle(Vector3.UnitY, -(float)Math.PI / 2f)
        };

        private static readonly Quaternion upwardDirection = Quaternion.CreateFromAxisAngle(new Vector3(1f, 0f, 0f), (float)Math.PI / 4f);

        private static readonly Quaternion downwardDirection = Quaternion.CreateFromAxisAngle(new Vector3(1f, 0f, 0f), -(float)Math.PI / 4f);

        private static readonly Vector3[] forwardVectors = new Vector3[4]
        {
            new Vector3(0f, 0f, -1f),
            new Vector3(-1f, 0f, 0f),
            new Vector3(0f, 0f, 1f),
            new Vector3(1f, 0f, 0f)
        };

        internal ComponentBody m_componentBody;

        public ComponentMFTrain ParentBody;

        protected ComponentDamage componentDamage;

        protected float m_outOfMountTime;

        public ComponentMount m_componentMount;

        private SubsystemBodies m_systemBodies;

        private SubsystemTerrain m_systemTerrain;

        private SubsystemPickables SubsystemPickables;

        public ComponentMFEngine componentEngine;

        public ComponentMFChestCache componentChestCache;

        private int m_forwardDirection;

        public Quaternion rotation;

        private Vector3 forwardVector;

        private bool isRunning
        {
            get
            {
                ComponentBody componentBody = m_componentBody;
                return componentBody != null && componentBody.Velocity.Length() > 4f;
            }
        }

        public int Direction
        {
            get
            {
                return m_forwardDirection;
            }
            set
            {
                forwardVector = forwardVectors[value];
                m_forwardDirection = value;
                rotation = directions[value];
            }
        }

        public UpdateOrder UpdateOrder => UpdateOrder.Default;

        public override void Load(ValuesDictionary valuesDictionary, IdToEntityMap idToEntityMap)
        {
            base.Load(valuesDictionary, idToEntityMap);
            m_componentBody = base.Entity.FindComponent<ComponentBody>(throwOnError: true);
            componentDamage = base.Entity.FindComponent<ComponentDamage>();
            m_componentMount = base.Entity.FindComponent<ComponentMount>(throwOnError: true);
            m_systemBodies = base.Project.FindSubsystem<SubsystemBodies>();
            m_systemTerrain = base.Project.FindSubsystem<SubsystemTerrain>();
            SubsystemPickables = base.Project.FindSubsystem<SubsystemPickables>();
            if ((componentEngine = base.Entity.FindComponent<ComponentMFEngine>()) != null)
            {
                ComponentBody componentBody = m_componentBody;
                componentBody.CollidedWithBody = (Action<ComponentBody>)Delegate.Combine(componentBody.CollidedWithBody, new Action<ComponentBody>(CollidedWithBody));
            }

            if ((componentChestCache = base.Entity.FindComponent<ComponentMFChestCache>()) != null)
            {
            }

            Direction = valuesDictionary.GetValue("Direction", 0);
        }

        public override void Save(ValuesDictionary valuesDictionary, EntityToIdMap entityToIdMap)
        {
            if (m_forwardDirection != 0)
            {
                valuesDictionary.SetValue("Direction", m_forwardDirection);
            }

            EntityReference value = EntityReference.FromId(ParentBody, entityToIdMap);
            if (!value.IsNullOrEmpty())
            {
                valuesDictionary.SetValue("ParentBody", value);
            }
        }

        public void CollidedWithBody(ComponentBody body)
        {
            if (!isRunning || MathUtils.Abs(body.Density - 9.76f) <= float.Epsilon)
            {
                return;
            }

            float num = (m_componentBody.Velocity.XZ - body.Velocity.XZ).LengthSquared() * 0.3f;
            if (!(num < 0.02f) && m_componentBody.Velocity.XZ.LengthSquared() != 0f)
            {
                ComponentHealth componentHealth = body.Entity.FindComponent<ComponentHealth>();
                if (componentHealth != null && componentHealth.Entity.FindComponent<ComponentPlayer>() == null)
                {
                    componentHealth.Injure(num / componentHealth.AttackResilience, null, ignoreInvulnerability: false, "被火车创死啦!");
                }
            }
        }

        public ComponentMFTrain FindNearestTrain()
        {
            DynamicArray<ComponentBody> dynamicArray = new DynamicArray<ComponentBody>();
            m_systemBodies.FindBodiesAroundPoint(m_componentBody.Position.XZ, 2f, dynamicArray);
            float num = 0f;
            ComponentMFTrain result = null;
            foreach (ComponentMFTrain item in dynamicArray.Select(GetRailEntity))
            {
                if (item == null || item.Entity == base.Entity)
                {
                    continue;
                }

                float num2 = 0f;
                if (item.m_componentBody.Velocity.LengthSquared() < 1f && item.Direction == Direction)
                {
                    Vector3 vector = item.m_componentBody.Position + Vector3.Transform(item.m_componentMount.MountOffset, item.m_componentBody.Rotation) - m_componentBody.Position;
                    if (vector.LengthSquared() <= 4f)
                    {
                        num2 = 4f - vector.LengthSquared();
                    }
                }

                if (num2 > num)
                {
                    num = num2;
                    result = item;
                }
            }

            return result;
        }

        public ComponentPlayer FindInteractingPlayer()
        {
            ComponentPlayer componentPlayer = base.Entity.FindComponent<ComponentPlayer>();
            if (componentPlayer == null)
            {
                ComponentBlockEntity componentBlockEntity = base.Entity.FindComponent<ComponentBlockEntity>();
                if (componentBlockEntity != null)
                {
                    Vector3 position = new Vector3(componentBlockEntity.Coordinates);
                    componentPlayer = base.Project.FindSubsystem<SubsystemPlayers>(throwOnError: true).FindNearestPlayer(position);
                }
            }

            return componentPlayer;
        }

        public ComponentPlayer FindNearestPlayer()
        {
            Vector3 position = new Vector3(m_componentBody.Position.X, m_componentBody.Position.Y, m_componentBody.Position.Z);
            return base.Project.FindSubsystem<SubsystemPlayers>(throwOnError: true).FindNearestPlayer(position);
        }

        public ComponentPlayer FindRidingPlayer()
        {
            return m_componentMount.Rider.Entity.FindComponent<ComponentPlayer>();
        }

        public void SetDirection(int value)
        {
            Direction = value;
            m_componentBody.Rotation = rotation;
        }

        public void Update(float dt)
        {
            if (componentEngine != null)
            {
                componentEngine.Coordinates = new Point3((int)m_componentBody.Position.X, (int)m_componentBody.Position.Y, (int)m_componentBody.Position.Z);
                componentEngine.Position = m_componentBody.Position;
            }

            if (m_componentMount.Rider != null)
            {
                ComponentPlayer componentPlayer = FindRidingPlayer();
                componentEngine.SetRidingPlayer(componentPlayer);
                componentPlayer.ComponentLocomotion.LookOrder = componentPlayer.ComponentInput.PlayerInput.Look;
                if (componentPlayer.ComponentGui.m_clothingButtonWidget.IsChecked && componentPlayer.ComponentGui.ModalPanelWidget is ClothingWidget)
                {
                    componentPlayer.ComponentGui.ModalPanelWidget = new MFEngineWidget(componentPlayer.ComponentMiner.Inventory, componentEngine, componentChestCache);
                }
            }
            else
            {
                componentEngine.SetRidingPlayer(null);
            }

            if (CommonLib.WorkType != WorkType.Client)
            {
                switch (Direction)
                {
                    case 0:
                    case 2:
                        m_componentBody.Position = new Vector3(MathUtils.Floor(m_componentBody.Position.X) + 0.5f, m_componentBody.Position.Y, m_componentBody.Position.Z);
                        break;
                    case 1:
                    case 3:
                        m_componentBody.Position = new Vector3(m_componentBody.Position.X, m_componentBody.Position.Y, MathUtils.Floor(m_componentBody.Position.Z) + 0.5f);
                        break;
                }
            }

            if (!m_componentBody.StandingOnValue.HasValue)
            {
                SimulateRail(Terrain.ExtractData(m_systemTerrain.Raycast(m_componentBody.Position, m_componentBody.Position + new Vector3(0f, -3f, 0f), useInteractionBoxes: false, skipAirBlocks: true, null).GetValueOrDefault().Value));
                m_componentBody.Rotation = rotation;
                m_componentBody.m_velocity -= new Vector3(0f, 100f, 0f) * dt;
            }

            float num = 0f;
            if (componentEngine != null)
            {
                num = componentEngine.Work;
            }

            if (num >= 1f && m_componentBody.StandingOnValue.HasValue)
            {
                float num2 = 4f * (num - 1f);
                TerrainRaycastResult valueOrDefault = m_systemTerrain.Raycast(m_componentBody.Position, m_componentBody.Position + new Vector3(0f, -3f, 0f), useInteractionBoxes: false, skipAirBlocks: true, null).GetValueOrDefault();
                if (Terrain.ExtractContents(valueOrDefault.Value) == 543 && (dt *= SimulateRail(Terrain.ExtractData(valueOrDefault.Value))) > 0f && m_componentMount.Rider != null)
                {
                    ComponentPlayer componentPlayer2 = m_componentMount.Rider.Entity.FindComponent<ComponentPlayer>();
                    if (componentPlayer2 != null)
                    {
                        PlayerInput playerInput = componentPlayer2.ComponentInput.PlayerInput;
                        float y = new Vector2(playerInput.Move.X, playerInput.Move.Z).Y;
                        float num3 = y * num2;
                        float num4 = 3f;
                        float num5 = m_componentBody.m_velocity.Length();
                        if (playerInput.Move.Z > 0f)
                        {
                            m_componentBody.m_velocity = num3 * rotation.ToForwardVector2();
                        }
                        else if (playerInput.Move.Z < 0f)
                        {
                            m_componentBody.m_velocity = num3 * rotation.ToForwardVector2();
                        }
                    }
                }
            }
            else if (num < 1f && m_componentBody.m_velocity.Length() - 0.06f > 0f && m_componentBody.StandingOnValue.HasValue)
            {
                SimulateRail(Terrain.ExtractData(m_systemTerrain.Raycast(m_componentBody.Position, m_componentBody.Position + new Vector3(0f, -3f, 0f), useInteractionBoxes: false, skipAirBlocks: true, null).GetValueOrDefault().Value));
                m_componentBody.m_velocity = (m_componentBody.m_velocity.Length() - 0.06f) * rotation.ToForwardVector2();
            }

            m_componentBody.Rotation = Quaternion.Slerp(m_componentBody.Rotation, rotation, 0.2f);
        }

        private float SimulateRail(int data)
        {
            if (MFRailBlock.IsBreaker(data))
            {
                return 0f;
            }

            int railType = MFRailBlock.GetRailType(data);
            if (MFRailBlock.IsCorner(railType))
            {
                if (GetOffsetOnDirection(m_componentBody.Position, m_forwardDirection) > 0.5f)
                {
                    Turn(railType);
                }

                return 50f;
            }

            if (MFRailBlock.IsDirectionX(railType) ^ !MFRailBlock.IsDirectionX(m_forwardDirection))
            {
                rotation = ((railType <= 5) ? directions[Direction] : ((railType - 6 != Direction) ? (directions[Direction] * upwardDirection) : (directions[Direction] * downwardDirection)));
                return (railType > 5 && railType - 6 != Direction) ? 30f : 50f;
            }

            return 0f;
        }

        private bool Turn(int turnType)
        {
            if (Direction == turnType)
            {
                Direction = (Direction - 1) & 3;
                m_componentBody.Velocity = MathUtils.Abs(m_componentBody.Velocity.X + m_componentBody.Velocity.Z) * forwardVector;
                m_componentBody.Position = Vector3.Floor(m_componentBody.Position) + center;
                return true;
            }

            if (((Direction - 1) & 3) == turnType)
            {
                Direction = (Direction + 1) & 3;
                m_componentBody.Velocity = MathUtils.Abs(m_componentBody.Velocity.X + m_componentBody.Velocity.Z) * forwardVector;
                m_componentBody.Position = Vector3.Floor(m_componentBody.Position) + center;
                return true;
            }

            return false;
        }

        private static float GetOffsetOnDirection(Vector3 vec, int direction)
        {
            float num = (((direction & 1) == 0) ? (vec.Z - MathUtils.Floor(vec.Z)) : (vec.X - MathUtils.Floor(vec.X)));
            return ((direction & 2) == 0) ? (1f - num) : num;
        }

        public static ComponentMFTrain GetRailEntity(Component c)
        {
            return c.Entity.FindComponent<ComponentMFTrain>();
        }

        public void ThrowAllItems()
        {
            Vector3 position = m_componentBody.Position;
            foreach (IInventory item in base.Entity.FindComponents<IInventory>())
            {
                item.DropAllItems(position);
            }
        }
    }
}